import org.testng.annotations.Test;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.AfterMethod;

import org.hibernate.cfg.Configuration;
import org.hibernate.SessionFactory;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.Session;
import org.hibernate.Query;
import java.util.List;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;

public class RankingPersistenceTest {
	private static SessionFactory sessionFactory = null;
	
	@BeforeClass
	public void setup() {
		Configuration cfg = new Configuration();
		cfg.configure();
		StandardServiceRegistryBuilder registry = new StandardServiceRegistryBuilder();
		registry.applySettings(cfg.getProperties());
		sessionFactory = cfg.buildSessionFactory(registry.build());
	}
	//@AfterMethod
	//public void shutdown(){
	//	sessionFactory.close();
	//}
	public void modelCreation()
	{
		Person subject = new Person();
		subject.setName("A.R. Rahman");
		System.out.println(subject);
		
		Person observer = new Person();
		observer.setName("Oscar");
		System.out.println(observer);
		
		Skill skill = new Skill();
		skill.setName("Western Music");
		System.out.println(skill);
		
		Ranking ranking = new Ranking();
		ranking.setSubject(subject);
		ranking.setObserver(observer);
		ranking.setSkill(skill);
		ranking.setRanking(1);
		
		System.out.println(ranking);
	}
	
	public void createPerson() {
		Person subject = new Person();
		subject.setName("A.R. Rahman");
		Session session = sessionFactory.openSession();
		Transaction txn = session.beginTransaction();
		session.persist(subject);
		txn.commit();

		Query query = session.createQuery("from Person p where p.name =:name");
		query.setParameter("name","A.R. Rahman");
		List<Person> persons = query.list();
		for(Person p : persons){
			System.out.println(p);
		}		
		session.close();
		
	}
	public void testSaveRanking(){
		Session session = sessionFactory.openSession();
		Transaction txn = session.beginTransaction();
		Person p = savePerson(session,"Siva");
		Person o = savePerson(session,"Gavin Camp");
		
		Skill s = saveSkill(session,"JAVA");
		Ranking ranking = new Ranking();
		ranking.setSubject(p);
		ranking.setObserver(o);
		ranking.setSkill(s);
		ranking.setRanking(7);
		session.save(ranking);
		txn.commit();
		session.close();		
	}
	public Person savePerson(Session session, String name){
		Person p = findPerson(session,name);
		if(p == null){
			p = new Person();
			p.setName(name);
			session.save(p);
		}
		return p;
	}
	public Skill saveSkill(Session session, String name){
		Skill s = findSkill(session,name);
		if(s == null){
			s = new Skill();
			s.setName(name);
			session.save(s);
		}
		return s;
	}
	public Person findPerson(Session session, String name){
		Query query = session.createQuery("from Person p where p.name =:name");
		query.setParameter("name",name);
		Person person = (Person)query.uniqueResult();
		return person;
	}
	public Skill findSkill(Session session, String name){
		Query query = session.createQuery("from Skill s where s.name =:name");
		query.setParameter("name",name);
		Skill s = (Skill)query.uniqueResult();
		return s;
	}
	private Ranking findRanking(Session session,String subject,String observer,String skill){
		Query query = session.createQuery("from Ranking r " +
					"where r.subject.name =:subject and " +
					"r.observer.name =:observer and "+
					"r.skill.name =:skill");
		query.setParameter("subject", subject);
		query.setParameter("observer", observer);
		query.setParameter("skill", skill);
		query.setMaxResults(1);
		Ranking r = (Ranking)query.uniqueResult();
		return r;
	}
    @Test
    public void changeRanking() {
        populateRankingData();
        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();
        Query query = session.createQuery("from Ranking r "
                + "where r.subject.name=:subject and "
                + "r.observer.name=:observer and "
                + " r.skill.name=:skill");
        query.setString("subject", "J. C. Smell");
        query.setString("observer", "Gene Showrama");
        query.setString("skill", "Java");
        Ranking ranking = (Ranking) query.uniqueResult();
        assertNotNull(ranking, "Could not find matching ranking");
        ranking.setRanking(9);
        tx.commit();
        session.close();
		int avg = getAverage("J. C. Smell", "Java");
		System.out.println();
		System.out.println("Average = " + avg);
		System.out.println();
        assertEquals(avg, 8);
    }
    @Test
	public void removeRanking() {
		populateRankingData();
		Session session = sessionFactory.openSession();
		Transaction tx = session.beginTransaction();
		Ranking r = findRanking(session, "J. C. Smell", "Gene Showrama", "Java");
		assertNotNull(r,"Ranking Not Found");
		session.delete(r);
		tx.commit();
		session.close();
		System.out.println();
		System.out.println(getAverage("J. C. Smell", "Java"));
	}
    private int getAverage(String subject, String skill) {
        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();

        Query query = session.createQuery("from Ranking r "
                + "where r.subject.name=:name "
                + "and r.skill.name=:skill");
        query.setString("name", subject);
        query.setString("skill", skill);
        int sum = 0;
        int count = 0;
        for (Ranking r : (List<Ranking>) query.list()) {
            count++;
            sum += r.getRanking();
            System.out.println(r);
        }
        int average = sum / count;
        tx.commit();
        session.close();
        return average;
    }
	
	public void createRanking(){
		Session session = sessionFactory.openSession();
		Transaction txn = session.beginTransaction();
		Person subject = new Person();
		subject.setName("A.R. Rahman");
		System.out.println(subject);
		
		Person observer = new Person();
		observer.setName("Oscar");
		System.out.println(observer);
		
		Skill skill = new Skill();
		skill.setName("Western Music");
		System.out.println(skill);
		
		Ranking ranking = new Ranking();
		ranking.setSubject(subject);
		ranking.setObserver(observer);
		ranking.setSkill(skill);
		ranking.setRanking(1);
	}

    public void testRankings() {
        populateRankingData();
        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();

        Query query = session.createQuery("from Ranking r "
                + "where r.subject.name=:name "
                + "and r.skill.name=:skill");
        query.setString("name", "J. C. Smell");
        query.setString("skill", "Java");
        int sum = 0;
        int count = 0;		
        for (Ranking r : (List<Ranking>) query.list()) {
            count++;
            sum += r.getRanking();
            System.out.println(r);
        }
        int average = sum / count;
		System.out.println("Sum = " + sum + " count = " + count + " average = " + average );

        tx.commit();
        session.close();
        assertEquals(count, 3);
        assertEquals(average, 7);
    }
	
    private void populateRankingData() {
        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();
        createData(session, "J. C. Smell", "Gene Showrama", "Java", 6);
        createData(session, "J. C. Smell", "Scottball Most", "Java", 7);
        createData(session, "J. C. Smell", "Drew Lombardo", "Java", 8);
        tx.commit();
        session.close();
    }	
	private void createData(Session session, String subjectName,
                            String observerName, String skillName, int rank) {
        Person subject = savePerson(session, subjectName);
        Person observer = savePerson(session, observerName);
        Skill skill = saveSkill(session, skillName);

        Ranking ranking = new Ranking();
        ranking.setSubject(subject);
        ranking.setObserver(observer);
        ranking.setSkill(skill);
        ranking.setRanking(rank);
        session.save(ranking);
    }
	
	public static void main(String[] args){
		RankingPersistenceTest test = new RankingPersistenceTest();
		test.setup();
		test.testSaveRanking();
	}
}